package com.yc154.web;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.yc.damai.entity.User;
import com.yc154.dao.UserDao;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

@Component
@ServerEndpoint("/talk/{uid}")
public class TalkDo {

    // 会话集合
    // 为什么使用静态:  Spring 会为每一个上线的用户创建一个 TalkDo 对象
    // 使用静态确保容器的唯一性 => Redis 保存
    private static Map<String, Session> sessionMap = new ConcurrentHashMap<>();
    private static ObjectMapper objectMapper = new ObjectMapper();

    // 1. TalkDo 会在每次连接建立时, Tomcat 或 Netty 服务器都会创建一个 TalkDo 对象
    // 2. 被 @Component 标注的类 TalkDo 也同时被 SpringIOC 容器管理
    // 3. Spring 不能对静态属性注入, 可以使用 @Autowired 标注的 setter 方法
    private static UserDao userDao;
    @Autowired
    public void setUserDao(UserDao userDao) {
        TalkDo.userDao = userDao;
    }

    // 连接建立成功调用的方法
    @OnOpen
    public void onOpen(@PathParam("uid") String id, Session session) throws IOException {
        // 查询用户信息
        User user = userDao.getUserById(Integer.parseInt(id));
        session.getUserProperties().put("uid", id);
        session.getUserProperties().put("user", user);

        sessionMap.put(id, session);
        System.out.println("上线: " + id);

        // 将已上线用户发送给自己
        List<Object> onlineUsers = sessionMap.values()
                .stream()
                .map(s -> (User)s.getUserProperties().get("user"))
                // 将自己(当前用户)过滤出来
                .filter(u->!u.equals(user))
                .collect(Collectors.toList());
        Message msg = new Message();
        msg.setType("allOnlineUsers");
        msg.setContent(toJson(onlineUsers));
        msg.setSender(user);
        // 发送消息给自己
        msg.setReceiver(user);
        send(msg);

        // TODO 测试群发
        msg = new Message();
        msg.setTimestamp(System.currentTimeMillis());
        msg.setContent("上线了");
//        User user = new User();
//        user.setUid(Integer.parseInt(id));
        msg.setSender(user);
        msg.setType("upline");
        send(msg);
    }

    // 连接关闭调用的方法
    @OnClose
    public void onClose(Session session) throws IOException {
        String id = session.getUserProperties().get("uid").toString();
        User user = (User) session.getUserProperties().get("user");
        sessionMap.remove(id);
        System.out.println("下线: " + id);
        // TODO 测试群发
        Message msg = new Message();
        msg.setTimestamp(System.currentTimeMillis());
        msg.setContent("下线了");
//        User user = new User();
//        user.setUid(Integer.parseInt(id));
        msg.setSender(user);
        msg.setType("downline");
        send(msg);
    }

    // 收到客户端消息后调用的方法
    @OnMessage
    public void onMessage(String message, Session session)
            throws IOException {
        // message 定义消息的格式 => json
        Message msg = parseJson(message, Message.class);
        send(msg);
    }

    private void send(Message msg) throws IOException {
        msg.setTimestamp(System.currentTimeMillis());
        if(msg.getReceiver() == null){
            // 发送给所有人, TODO 测试用
            for (Session session : sessionMap.values()) {
                session.getBasicRemote().sendText(toJson(msg));
            }
        } else {
            // 私发
            Integer uid = msg.getReceiver().getUid();
            Session session = sessionMap.get(uid.toString());
            // session 不为null才发送消息
            if (session!=null) {
                session.getBasicRemote().sendText(toJson(msg));
            }
        }
    }

    private <T> T parseJson(String json, Class<T> clazz) {
        try {
            return objectMapper.readValue(json, clazz);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }

    private <T> String toJson(T t) {
        try {
            return objectMapper.writeValueAsString(t);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }
}

// 消息体结构
@Data
@AllArgsConstructor
@NoArgsConstructor
class Message {
    private Integer id;
    private Long timestamp;	  //发送时间
    private User sender;      //发送人
    private User receiver;    //接受人
    private String type;      //消息类型(文字,图片,视频,文档,指令[上线,下线])
    private String content;   //消息内容
}

// 房间(群)对象
class Group{
    private Integer id;
    private String name;       //房间名,群名
    private User myinfo;       //创建人
    private Date createTime;   //创建时间
    private Set<User> receivers;     //接收人群
    private List<Message> msgs;         //消息列表
}
